package logic.gridSimInterface.brokers;

import eduni.simjava.Sim_event;
import gridsim.GridSim;
import gridsim.GridSimTags;
import gridsim.Gridlet;
import gridsim.ResourceCharacteristics;
import gridsim.net.SimpleLink;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Vector;
import logic.JSimulation;
import logic.SimulationParameters;
import logic.gridSimInterface.SimTags;
import logic.gridSimInterface.resource.ResourceCharacteristicExt;
import logic.gridSimInterface.user.GridletInfo;
import logic.schedulers.AbstractGlobalScheduling;
import logic.schedulers.AlgorithmCollection;
import logicTests.Dbg;

/**
 * Trieda hierarchickeho brokera, tj. brokera, ktory je nadradeny dalsim
 * brokerom. Po obdrzani spravy GRIDLETINFO_SUBMIT v dynamickom planovani
 * vyhlada pomocou modulu globalneho planovania prisluchajuceho brokera,
 * ktoremu spravu preposle dalej. V statickom planovani najskor pozbiera
 * informacie od ulohach od vsetkych uzivatelov, az potom ich zacne spracovavat.
 */
public class BrokerHierarchical extends AbstractBroker {
    private Vector<Integer> resourcesID;
    private LinkedList<ResourceCharacteristicExt> resourcesInfo = null;
    private HashMap<Integer, Integer> resIDtoBrokerID;
    private int planning = 0;

    //v statickom planovani - pocet potvrdenich prijati gridletInfo
    //potrebne na to, aby sa vedel, kedy uz prisli vsetky ulohy a mohlo
    //sa odstartovat planovanie
    private int gridletInfosRecieved = 0;
    private LinkedList<GridletInfo> staticGridletInfos = null;
    /**
     * Mena podriadenych brokerov
     */
    private Vector<String> subBrokers;

    public BrokerHierarchical(String name, SimpleLink link, String owner) 
        throws Exception {
        
        super(name, link, owner);
        subBrokers = new Vector<String>();
        staticGridletInfos = new LinkedList<GridletInfo>();
        gridletInfosRecieved = 0;
    }

    protected Vector<String> getSubBrokers() {
        return subBrokers;
    }

    /**
     * Pomocou modulu globalneho planovania vyberie pre danu ulohu vhodny.
     * Zisti, ktoremu brokerovi je zdroj priradeny a preposle mu spravu
     * GRIDLETINFO_SUBMIT. Ak pre ulohu neexistuje vhodny zdroj, odosle
     * uzivatelovi spravu o nevykonatelnosti ulohy.
     */
    protected void handleDynamicSubmit(GridletInfo g) {
        AbstractGlobalScheduling ags = SimulationParameters.getGlobalScheduler();
        int resID = ags.findResource(g, resourcesInfo);
        if (resID == -1)
            sendUnableToProcessMessage(g);
        else {
            int brokerID = resIDtoBrokerID.get(new Integer(resID));
            System.out.println(get_name() + " send gridlet info " + g.getGridletId() + " to broker " + brokerID );
            super.send(brokerID,
                    GridSimTags.SCHEDULE_NOW,
                    SimTags.GRIDLETINFO_SUBMIT,
                    g);
        };
    }

    /**
     * Metoda sa zvola, ked uz ma broker k dispozicii informacie o vsetkych
     * ulohach. Pomocou vybraneho modulu globalneho planovania vyberie
     * zdroje pre dane ulohy. Potom rozdeli ulohy prisluchajucim brokerom
     * a odosle im ich v sprave GRIDLETINFO_SUBMIT. Ak ku niektorej ulohe
     * neexistuje vhodny zdroj, odosle uzivatelovi spravu o jej nevykonatelnosti.
     */
    protected void handleStaticSubmit(LinkedList<GridletInfo> g) {
        System.out.println(get_name() + " handleStaticSubmit - g.size() " + g.size());
        AbstractGlobalScheduling ags = SimulationParameters.getGlobalScheduler();

        HashMap<Integer, LinkedList<GridletInfo>> brokerGridlets =
                new HashMap<Integer, LinkedList<GridletInfo>>();
        
        for (String subBroker : subBrokers) {
            int brokerID = GridSim.getEntityId(subBroker);
            brokerGridlets.put(new Integer(brokerID), 
                    new LinkedList<GridletInfo>());
        }
        
        //ulohy sa usporiadaju do vektora, aby ich poradie bolo pevne
        Vector<GridletInfo> tasks = new Vector<GridletInfo>(g.size());
        for (GridletInfo elem : g)
            tasks.add(elem);
        
        Vector<Integer> resIDs = ags.findResources(tasks, resourcesInfo);
        for (int i = 0; i < resIDs.size(); i++) {
            int resID = resIDs.get(i);
            GridletInfo task = tasks.get(i);
            if (resID == -1)
                sendUnableToProcessMessage(task);
            else {
                int brokerID = resIDtoBrokerID.get(new Integer(resID));
                LinkedList<GridletInfo> brokerIDgridlets =
                        brokerGridlets.get(new Integer(brokerID));
                brokerIDgridlets.add(task);
            };
        }
        
        for (Integer brokerID : brokerGridlets.keySet()) {
            LinkedList<GridletInfo> gridletInfos = brokerGridlets.get(brokerID);
                super.send(brokerID,
                        GridSimTags.SCHEDULE_NOW,
                        SimTags.GRIDLETINFO_SUBMIT,
                        gridletInfos);
        }
    }
    
    
    public void addSubBroker(String name) {
        subBrokers.add(name);
    }

    public void body() {
        Sim_event ev = new Sim_event();
        planning = SimulationParameters.getPlanningType();
        
        ////////////////////////////////////////////////////////////////////////
        //CAKANIE NA SPRAVU "SIMULATION_START"
        waitForStart();
        Dbg.vypis("Broker " + get_name() + 
                  " recieved start at " + GridSim.clock());

        ////////////////////////////////////////////////////////////////////////
        //ODOSLANIE "SIMULATION_START" PODRIADENYM BROKEROM
        String subBrNames = "";
        for(String subBrokerName : subBrokers) {
            super.send(subBrokerName,
                    GridSimTags.SCHEDULE_NOW,
                    SimTags.SIMULATION_START);
            subBrNames = subBrNames + " " + subBrokerName;
        }
        Dbg.vypis("Broker " + get_name() + 
                  " sended SIM_START to " + subBrNames);
        
        ////////////////////////////////////////////////////////////////////////
        //CAKANIE NA "BROKER_READY" OD VSETKYCH PODRIADENYCH BROKEROV
        //- zaroven ziska od nich ID ich podriadenych zdrojov
        resourcesID = new Vector<Integer>();
        resIDtoBrokerID = new HashMap<Integer,Integer>();
        for (int i = 0; i < subBrokers.size(); ) {
            sim_get_next(ev);
            if (ev.get_tag() == SimTags.BROKER_READY) {
                i++;
                Vector<Integer> newResID = (Vector) ev.get_data();
                resourcesID.addAll(newResID);
                for(Integer resID : newResID)
                    resIDtoBrokerID.put(new Integer(resID), 
                                          new Integer(ev.get_src()));
            }
        }
        Dbg.vypis("Broker " + get_name() + " recieved BROKER_READY from" +
                "all subbrokers at " + GridSim.clock());

        ////////////////////////////////////////////////////////////////////////
        //NAPLNENIE INFORMACII O ZDROJOCH
        resourcesInfo = getResourcesInfo(resourcesID);
        Dbg.vypis("Entity " + this.get_name() + " recieved complete " +
                "grid resource list (size = " + resourcesInfo.size() +")" +
                " at time " + GridSim.clock());
        sendReadyToOwner(resourcesID);        
        
        ////////////////////////////////////////////////////////////////////////
        //OBSLUHA PRICHADZAJUCICH UDALOSTI
        

        boolean end = false;
        while (!end)
        {
            sim_get_next(ev);
            switch(ev.get_tag()) {        	
                case SimTags.GRIDLETINFO_SUBMIT:
                    handleGridletinfoSumbit(ev);
                    break;    				

                case SimTags.RESOURCE_OFFLINE:
                    resourceStatusChanged((Integer)ev.get_data(), false);
                    break;
                
                case SimTags.RESOURCE_ONLINE:
                    resourceStatusChanged((Integer)ev.get_data(), true);
                    break;
                
                case SimTags.USER_OFFLINE:
                    userStatusChanged((Integer)ev.get_data(), false);
                    break;
                
                case SimTags.USER_ONLINE:
                    userStatusChanged((Integer)ev.get_data(), true);
                    break;
                
                case GridSimTags.END_OF_SIMULATION:
                    end = true; 
                    break;
                default:
                    if (!handleMessage(ev))
                        System.out.println("Warnng: Entity " + get_name() +
                                " recieved unknown message " +
                                ev.get_tag());
            }
        }
        
        super.terminateIOEntities();
    }
    
    private void handleGridletinfoSumbit(Sim_event ev) {
        if (planning == SimTags.STATIC_PLANNING) {
            staticGridletInfos.addAll((LinkedList<GridletInfo>)ev.get_data());
            gridletInfosRecieved++;
            if (gridletInfosRecieved == JSimulation.getUsersCount())
                handleStaticSubmit(staticGridletInfos);
        } else {
            handleDynamicSubmit((GridletInfo)ev.get_data());
        };
    }
}
